﻿#region usings
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Markup;
using System.Xaml;

#endregion
namespace InsuranceAdjuster.Helpers {
    // Convention: <yourName>Extension
    public class MethodInvokeExtension : IMarkupExtension<object> {

        // Object to call the Method upon (ViewModel)
        private object _target;

        // The Method to call on _target when the event fires
        private MethodInfo _targetMethod;
        

        /// <summary>
        /// Method to call when event fires
        /// </summary>
        /// <remarks>
        /// SelectionChanged="{yourxmlns:MethodInvoke  
        ///                   Method=OnAppointmentChanged
        /// </remarks>
        public string Method { get; set; }


        public object ProvideValue(IServiceProvider serviceProvider) {
            try {
                Delegate del = null;

                // IProvideValueTarget - Provides information about the target object
                IProvideValueTarget targetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
    
                // IXamlTypeResolver - Handles types that use xml prefixes
                IXamlTypeResolver xamlResolver = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;
                
                // IRootObjectProvider - Provides access to the root object (the Framework Element)
                IRootObjectProvider rootObject = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;

                if (targetProvider != null) {
                    // Get the target element (ex: ListBox)
                    var targetObject = targetProvider.TargetObject;
                    
                    // Get the target element's event info
                    EventInfo targetEvent = targetProvider.TargetProperty as EventInfo;

                    if (targetEvent != null) {
                        // Get the event's parameters and types for the target element
                        ParameterInfo[] pars = targetEvent.GetAddMethod().GetParameters();
                        Type delegateType = pars[0].ParameterType;
                        MethodInfo invoke = delegateType.GetMethod("Invoke");
                        pars = invoke.GetParameters();

                        // Create the function call (to invoke the event handler)
                        Type customType = typeof(MethodInvokeExtension);
                        var nonGenericMethod = customType.GetMethod("PrivateHandlerGeneric");
                        MethodInfo genericMethod = nonGenericMethod.MakeGenericMethod(pars[1].ParameterType);

                        // Grab the root FrameworkElement
                        if (rootObject != null) {
                            FrameworkElement rootObjFE = rootObject.RootObject as FrameworkElement;

                            if (rootObjFE != null) {
                                // Grab the FrameworkElement's DataContext and the method name exposed by it
                                _target = rootObjFE.DataContext;
                                _targetMethod = _target.GetType().GetMethod(Method);

                                // Make sure the FE's DataContext has the Method name or get out. 
                                if (_target == null) return null;
                                if (_targetMethod == null) return null;

                                // Create the event handler and attach it from the target element to the DataContext's method name
                                del = Delegate.CreateDelegate(delegateType, this, genericMethod);
                                //targetEvent.AddEventHandler(targetObject, del);
                            }
                        }
                    }
                }

                return del;
            } catch (Exception ex) {
                string innerex = ex.InnerException.ToString();
                return null;
                
            }
        }

        // Invoke the generic handler
        public void PrivateHandlerGeneric<T>(object sender, T e) {
            _targetMethod.Invoke(_target, null);
        }
    }
}
